-module(backtrack).

-export([
         behaviour_info/1,
         search/2,
         full_log/5,
         no_log/5
        ]).


behaviour_info(callbacks) ->
    [{is_goal, 1},
     {children, 1},
     %% {is_failure, 1},
     {log, 5}
    ].


search(BTMod, StartState) ->
    bt_fwd(BTMod, [StartState], [StartState], [], StartState).


bt_fwd(BTMod, SL, [], DE, CS) ->
    BTMod:log(fail, SL, [], DE, CS),
    fail;
bt_fwd(BTMod, SL, NSL, DE, CS) ->
    BTMod:log(fwd, SL, NSL, DE, CS),
    %% FAIL: The following pattern (and indeed, most of these patterns)
    %% only really work if you use parameterized modules OR, a 
    %% SearchSettings opaque datablob that comes with BTMod (these
    %% two are tantamount to the same thing).
    case BTMod:is_goal(CS) of
        true ->
            SL;
        false ->
            %% TODO: Use a seen list.
            case BTMod:children(CS) -- (SL ++ NSL ++ DE) of
                [] ->
                    bt_back(BTMod, SL, NSL, DE, CS);
                NewChildren = [First|_] ->
                    bt_fwd(BTMod, [First|SL], NewChildren ++ NSL, DE, First)
            end
    end.


bt_back(BTMod, [CS|SL2], NSL, DE, CS) ->
    BTMod:log(back, [CS|SL2], NSL, DE, CS),
    case tl(NSL) of
        [] ->
            fail;
        NSL2 ->
            bt_back(BTMod, SL2, NSL2, [CS|DE], hd(NSL2))
    end;
bt_back(BTMod, OtherSL, NSL, DE, CS) ->
    BTMod:log(switching, OtherSL, NSL, DE, CS),
    bt_fwd(BTMod, [CS|OtherSL], NSL, DE, CS).


no_log(_, _, _, _, _) -> ok.


full_log(Mode, SL, NSL, DE, CS) ->
    io:format(
      "~p~nCS : ~p~nSL : ~p~nNSL : ~p~nDE : ~p~n~n", 
      [Mode, CS, SL, NSL, DE]
     ).

